package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.model.bm.DiagramPrivateAndDesginData;
import com.uinnova.product.eam.model.constants.AutoDrawConstant;
import com.uinnova.product.eam.model.vo.CiClassCiInfosVo;
import com.uinnova.product.eam.model.AutoDrawCIVo;
import com.uinnova.product.eam.service.AutoDrawDiagramSvc;
import com.uinnova.product.eam.service.FxDiagramSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.es.IamsESCIDesignSvc;
import com.uinnova.product.eam.service.es.IamsESCIPrivateSvc;
import com.uinnova.product.vmdb.comm.model.ci.*;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uino.bean.cmdb.base.ESCIClassInfo;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESAttrBean;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.bean.permission.base.SysUser;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.cmdb.dataset.ESDataSetSvc;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AutoDrawDiagramSvcImpl implements AutoDrawDiagramSvc {

    @Autowired
    ESDataSetSvc esDataSetSvc;

    @Autowired
    ESCIClassSvc esciClassSvc;

    @Autowired
    IamsESCIDesignSvc iamsESCIDesignSvc;

    @Autowired
    IamsESCIPrivateSvc iamsESCIPrivateSvc;

    @Autowired
    IEamCIClassApiSvc iEamCIClassApiSvc;

    @Autowired
    FxDiagramSvc fxDiagramSvc;

    @Autowired
    IamsCIRltSwitchSvc ciRltSwitchSvc;

    @Value("${auto.deployment.arch.diagram.sort:0-1-2-3}")
    private String autoDeploymentArchDiagramSort;

    @Override
    public AutoDrawCIVo getTechnologyArchDiagramData(Long dataSetId, String ciCode) {
        // 校验当前 CI 是否归属于数据集中心分类
        ESCIInfo esciInfo = this.checkCIValidity(dataSetId, ciCode);

        /*
         *  技术架构图生成逻辑
         *   根据 元模型中 应用系统 -> 应用模块 -> 技术组件 成图
         *   1.当前应用系统下的应用模块应用模块下的技术组件
         *   2.应用系统下的应用模块为空查询应用系统下的技术组件
         * */

        List<CcCiInfo> data = new ArrayList<>();

        // 获取中心对象编号
        String serialNum = esciInfo.getAttrs().get("编号").toString();
        // 根据1.2两种情况查询数据
        List<ESCIInfo> appModuleData = this.getAppModuleData(serialNum);

        Set<ESCIInfo> tmpData = this.getAppComponentData(appModuleData, serialNum);

        List<ESCIInfo> collect = tmpData.stream().collect(Collectors.toList());
        collect.sort(Comparator.comparing(ci -> ci.getAttrs().get("编号").toString()));


        // data数据转化为CcCiInfo 此处因为业务逻辑上查询的是 应用组件 数据 直接可以通过名称查询 属性信息 后期需求完善需要灵活查询
        CcCiClassInfo classByName = this.getCcClassByName("Draw.应用组件");
        for (ESCIInfo esciInfo1 : collect) {
            data.add(EamUtil.coverESCIInfo(esciInfo1, classByName.getAttrDefs(), classByName.getCiClass()));
        }

        Map<String, List<CcCiInfo>> result = data.stream().collect(Collectors.groupingBy(e -> e.getAttrs().get("组件类型")));
        // 预制空数据结构
        Map<String, List<CcCiInfo>> childData = new LinkedHashMap<String, List<CcCiInfo>>() {{
            put("Web服务层", CollectionUtils.isEmpty(result.get("Web服务层")) ? new ArrayList<>() : result.get("Web服务层"));
            put("应用层", CollectionUtils.isEmpty(result.get("应用层")) ? new ArrayList<>() : result.get("应用层"));
            put("中间件层", CollectionUtils.isEmpty(result.get("中间件层")) ? new ArrayList<>() : result.get("中间件层"));
            put("数据库层", CollectionUtils.isEmpty(result.get("数据库层")) ? new ArrayList<>() : result.get("数据库层"));
            put("操作系统层", CollectionUtils.isEmpty(result.get("操作系统层")) ? new ArrayList<>() : result.get("操作系统层"));
        }};

        // 重组数据
        List<Object> CIVo = new ArrayList<>();
        for (String groupData : childData.keySet()) {
            AutoDrawCIVo childCIVo = new AutoDrawCIVo();
            childCIVo.setName(groupData);
            List<Object> childList = new ArrayList<>();
            for (CcCiInfo ccCiInfo : childData.get(groupData)) {
                AutoDrawCIVo cChildCIVo = new AutoDrawCIVo();
                cChildCIVo.setEsciInfo(ccCiInfo);
                cChildCIVo.setName(ccCiInfo.getAttrs().get("中文名称"));
                childList.add(cChildCIVo);
            }
            childCIVo.setChildren(childList);
            CIVo.add(childCIVo);
        }
        AutoDrawCIVo autoDrawCIVo = new AutoDrawCIVo();
        // 此处写死名称原因同上
        CcCiClassInfo systemClass = this.getCcClassByName("Draw.应用系统");
        autoDrawCIVo.setEsciInfo(EamUtil.coverESCIInfo(esciInfo, systemClass.getAttrDefs(), systemClass.getCiClass()));
        autoDrawCIVo.setChildren(CIVo);
        autoDrawCIVo.setName(esciInfo.getAttrs().get("系统名称").toString());
        return autoDrawCIVo;

    }

    @Override
    public Map<String, Object> getContextArchDiagramData(Long dataSetId, String ciCode) {
        return Collections.emptyMap();
    }

    @Override
    public AutoDrawCIVo getFunctionalArchDiagramData(Long dataSetId, String ciCode) {
        ESCIInfo esciInfo = this.checkCIValidity(dataSetId, ciCode);

        AutoDrawCIVo autoDrawCIVo = new AutoDrawCIVo();
        /*
         *  功能架构图生成逻辑
         *   根据 元模型中 应用系统 -> 应用模块 -> 应用模块的所属应用模块 成图
         * */
        String serialNum = esciInfo.getAttrs().get("编号").toString();

        List<ESCIInfo> appModuleData = this.getAppModuleData(serialNum);

        // 返回值添加最上级
        autoDrawCIVo.setEsciInfo(esciInfo);
        autoDrawCIVo.setChildren(new ArrayList<>());
        autoDrawCIVo.setName(esciInfo.getAttrs().get("系统名称").toString());

        // 所有属于系统的模块根据层级进行包含关系处理
        if (!CollectionUtils.isEmpty(appModuleData)) {

            // 根据层级字段将数据分组
            Map<Integer, List<ESCIInfo>> moduleLvlMap = appModuleData.stream().collect(Collectors.groupingBy(e -> Integer.valueOf(e.getAttrs().get("模块层级").toString())));

            List<Object> child0 = new ArrayList<>();        // 首级的包含数据
            // 先搞个简单点的 固定写成三级数据
            for (ESCIInfo esciInfoLvl0 : CollectionUtils.isEmpty(moduleLvlMap.get(0)) ? new ArrayList<ESCIInfo>() : moduleLvlMap.get(0)) {
                List<Object> child1 = new ArrayList<>();
                AutoDrawCIVo autoDrawCIVo0 = new AutoDrawCIVo();
                autoDrawCIVo0.setName(esciInfoLvl0.getAttrs().get("模块名称").toString());
                autoDrawCIVo0.setEsciInfo(esciInfoLvl0);

                for (ESCIInfo esciInfoLvl1 : CollectionUtils.isEmpty(moduleLvlMap.get(1)) ? new ArrayList<ESCIInfo>() : moduleLvlMap.get(1)) {
                    if (esciInfoLvl1.getAttrs().get("上级应用模块").toString().equals(esciInfoLvl0.getAttrs().get("编号").toString())) {
                        AutoDrawCIVo autoDrawCIVo1 = new AutoDrawCIVo();
                        autoDrawCIVo1.setName(esciInfoLvl1.getAttrs().get("模块名称").toString());
                        autoDrawCIVo1.setEsciInfo(esciInfoLvl1);

                        List<Object> child2 = new ArrayList<>();
                        for (ESCIInfo esciInfoLvl2 : CollectionUtils.isEmpty(moduleLvlMap.get(2)) ? new ArrayList<ESCIInfo>() : moduleLvlMap.get(2)) {
                            if (esciInfoLvl2.getAttrs().get("上级应用模块").toString().equals(esciInfoLvl1.getAttrs().get("编号").toString())) {
                                AutoDrawCIVo autoDrawCIVo2 = new AutoDrawCIVo();
                                autoDrawCIVo2.setName(esciInfoLvl2.getAttrs().get("模块名称").toString());
                                autoDrawCIVo2.setEsciInfo(esciInfoLvl2);
                                child2.add(autoDrawCIVo2);
                            }
                        }
                        autoDrawCIVo1.setChildren(child2);
                        child1.add(autoDrawCIVo1);
                    }
                }
                autoDrawCIVo0.setChildren(child1);
                child0.add(autoDrawCIVo0);
            }
            autoDrawCIVo.setChildren(child0);
//            for (int i = moduleLvlMap.size(); i > 0; i--) {
//                // 根据所属上级模块分组
//                List<ESCIInfo> lvlESData = moduleLvlMap.get(i);
//
//                Map<Integer, List<ESCIInfo>> lvlMap = lvlESData.stream().collect(Collectors.groupingBy(esInfo -> Integer.valueOf(esInfo.getAttrs().get("上级应用模块").toString())));
//
//
//
//                AutoDrawCIVo lvlAutoDrawCIVo = new AutoDrawCIVo();
//                lvlAutoDrawCIVo.setEsciInfo(esciInfo);
//                lvlAutoDrawCIVo.setChildren(new ArrayList<>());
//                lvlAutoDrawCIVo.setName(esciInfo.getAttrs().get("系统名称").toString());
//
//            }
            // 查询所属模块属于上级所属模块字段的数据
        }

        return autoDrawCIVo;
    }

    @Override
    public AutoDrawCIVo getApplicationArchDiagramData(Long dataSetId, String ciCode) {
        ESCIInfo esciInfo = this.checkCIValidity(dataSetId, ciCode);

        AutoDrawCIVo autoDrawCIVo = new AutoDrawCIVo();
        /*
         *  应用架构图生成逻辑
         *   根据 元模型中 应用系统 -> (应用模块) -> (应用组件) -> 应用服务 -> 服务接口 成图
         *   框图展示应用系统到应用服务的关系 跳转到线图展示应用服务到服务接口的关系
         * */
        String serialNum = esciInfo.getAttrs().get("编号").toString();

        List<ESCIInfo> appModuleData = this.getAppModuleData(serialNum);

        // 返回值添加最上级
        autoDrawCIVo.setEsciInfo(esciInfo);
        autoDrawCIVo.setDrillDown(false);
        autoDrawCIVo.setChildren(new ArrayList<>());
        autoDrawCIVo.setName(esciInfo.getAttrs().get("系统名称").toString());

        // List<ESCIInfo> appInterfaceData = new ArrayList<>();

        // 获取应用系统下的应用组件数据 包括应用系统下应用模块的组件数据 和 应用系统直接关联的组件数据
        Set<ESCIInfo> appComponentData = this.getAppComponentData(appModuleData, serialNum);

        // 获取应用系统下的应用服务数据 包括应用系统下应用模块下应用组件的服务数据 和 应用系统直接关联的服务数据
        Set<ESCIInfo> appServiceData = this.getAppServiceData(appComponentData, serialNum);

        // 获取应用服务下的服务接口数据
        List<ESCIInfo> appInterfaceData = this.getAppInterfaceData(appServiceData);
        Map<String, List<ESCIInfo>> result = appInterfaceData.stream().collect(Collectors.groupingBy(e -> e.getAttrs().get("所属应用服务").toString()));

        List<Object> child = new ArrayList<>();
        for (ESCIInfo esciInfo1 : appServiceData) {
            AutoDrawCIVo childAutoVo = new AutoDrawCIVo();
            childAutoVo.setName(esciInfo1.getAttrs().get("服务名称").toString());
            childAutoVo.setEsciInfo(esciInfo1);
            List<Object> childList = new ArrayList<>();
            if (!BinaryUtils.isEmpty(result.get(esciInfo1.getAttrs().get("编号").toString()))) {
                for (ESCIInfo esciInfo2 : result.get(esciInfo1.getAttrs().get("编号").toString())) {
                    childList.add(esciInfo2);
                }
                childAutoVo.setChildren(childList);
                childAutoVo.setDrillDown(true);
            } else {
                childAutoVo.setChildren(new ArrayList<>());
                childAutoVo.setDrillDown(false);
            }
            child.add(childAutoVo);
        }
        autoDrawCIVo.setChildren(child);
        return autoDrawCIVo;
    }

    /**
     *  校验当前 CI 是否归属于数据集中心分类
     * @return
     */
    public ESCIInfo checkCIValidity (Long dataSetId, String ciCode) {
        // 必填项校验
        Assert.notNull(ciCode, "CI_CODE不能为空!");
        Assert.notNull(dataSetId, "数据集ID不能为空!");

        // 根据数据集ID 获取其中的中心分类的classCode
        JSONObject esDataSetSvcById = esDataSetSvc.getById(dataSetId);
        if (BinaryUtils.isEmpty(esDataSetSvcById)) {
            throw new BinaryException("数据集不存在!");
        }
        String dataSetClassId = esDataSetSvcById.get("classId").toString();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("ciCode.keyword", ciCode));
        List<ESCIInfo> esInfoByCode = iamsESCIDesignSvc.getListByQuery(boolQueryBuilder);
        if (CollectionUtils.isEmpty(esInfoByCode)) {
            throw new BinaryException("当前CI_CODE在设计库不存在!");
        }
        if (!dataSetClassId.equals(esInfoByCode.get(0).getClassId().toString())) {
            throw new BinaryException("无法根据当前CI_CODE自动生成视图!");
        }
        return esInfoByCode.get(0);
    }

    /**
     *  根据分类名称获取分类ID
     * @return
     */
    public Long getClassIdByName (String className) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("classCode.keyword", className));
        List<ESCIClassInfo> listByQuery = esciClassSvc.getListByQuery(boolQueryBuilder);
        if (CollectionUtils.isEmpty(listByQuery)) {
            throw new BinaryException("分类名称不存在");
        }
        return listByQuery.get(0).getId();
    }

    @Override
    public List<CiClassCiInfosVo> getDeploymentArchDiagramData(Long dataSetId, String ciCode) {
        // 校验当前 CI 是否归属于数据集中心分类
        ESCIInfo esciInfo = this.checkCIValidity(dataSetId, ciCode);
        /**
         *  部署架构图生成逻辑：
         *      根据元模型 应用系统 -> 服务(应用服务、技术服务) -> 部署单元[数据中心-网络区域-部署单元-部署实例] 成图
         *      1.当前应用层下的应用域的应用系统；
         *      2.当前应用域的应用系统；
         *      3.当前应用系统；
         *      4.条件1，2，3应用系统的部署单元的数据中心-网络区域-部署单元-部署实例。
         */
        ESCIClassInfo ciClassInfo = iEamCIClassApiSvc.queryClassById(esciInfo.getClassId());
        //step 1：获取应用系统编号
        List<String> appSysSerialNums = new ArrayList<>();
        List<ESCIInfo> appSysCis;
        switch (ciClassInfo.getClassName()) {
            case "Draw.应用层" :
                //1.查应用域
                String appLevelSerialNum = esciInfo.getAttrs().get("编号").toString();
                List<ESCIInfo> appDomainCis = queryCisWithOrAttrs(
                        Collections.singletonList(appLevelSerialNum), "所属应用层", getClassByName("Draw.应用域").getId());
                //2.查应用系统
                List<String> appDomainSerialNums = this.getCiSerialNumsAttrs(appDomainCis);
                appSysCis = queryCisWithOrAttrs(appDomainSerialNums, "所属应用域", getClassByName("Draw.应用系统").getId());
                appSysSerialNums = this.getCiSerialNumsAttrs(appSysCis);
                break;
            case "Draw.应用域" :
                //查应用系统
                String appDomainSerialNum = esciInfo.getAttrs().get("编号").toString();
                appSysCis = queryCisWithOrAttrs(Collections.singletonList(
                        appDomainSerialNum), "所属应用域", getClassByName("Draw.应用系统").getId());
                appSysSerialNums = this.getCiSerialNumsAttrs(appSysCis);
                break;
            case "Draw.应用系统" :
                appSysSerialNums.add(esciInfo.getAttrs().get("编号").toString());
                break;
            default:
                return new ArrayList<>();
        }
        //step 2：应用系统编号对应的部署单元
        ESCIClassInfo depolyUnitClassInfo = getClassByName("Draw.部署单元");
        List<ESCIInfo> depolyUnitCis = queryCisWithOrAttrs(appSysSerialNums, AutoDrawConstant.BELONGS_APP_SYSTEM, depolyUnitClassInfo.getId());
        if (CollectionUtils.isEmpty(depolyUnitCis)) {
            return new ArrayList<>();
        }
        List<String> belongNetworkAreas = depolyUnitCis.stream().map(ESCIInfo::getAttrs)
                .filter(e -> e.get(AutoDrawConstant.BELONGS_NETWORK_AREA) != null).map(e -> e.get(AutoDrawConstant.BELONGS_NETWORK_AREA).toString()).collect(Collectors.toList());

        //step 3：部署单元所属网络区域
        ESCIClassInfo networkAreaClassInfo = getClassByName("Draw.网络区域");
        List<ESCIInfo> networkAreaCis = queryCisWithOrAttrs(belongNetworkAreas, "编号", networkAreaClassInfo.getId());
        List<String> belongDataCenters =  networkAreaCis.stream().map(ESCIInfo::getAttrs)
                .filter(e -> e.get(AutoDrawConstant.BELONGS_DATACENTER) != null).map(e -> e.get(AutoDrawConstant.BELONGS_DATACENTER).toString()).collect(Collectors.toList());

        //step 4：网络区域所属数据中心
        ESCIClassInfo dataCenterClassInfo = getClassByName("Draw.数据中心");
        List<ESCIInfo> dataCenterCis = queryCisWithOrAttrs(belongDataCenters, "编号", dataCenterClassInfo.getId());
        if (CollectionUtils.isEmpty(depolyUnitCis)) {
            return new ArrayList<>();
        }

        //step 5：部署单元对应的部署实例
//        ESCIClassInfo depolyInstanceClassInfo = getClassByName("Draw.部署实例");
//        List<String> depolyUnitSerialNums = this.getCiSerialNumsAttrs(depolyUnitCis);
//        List<ESCIInfo> depolyInstanceCis = queryCisWithOrAttrs(depolyUnitSerialNums, "所属部署单元", depolyInstanceClassInfo.getId());

        //step 6：构造返回结果
        return this.constructResult(depolyUnitCis, networkAreaCis, dataCenterCis);
    }

    @Override
    public List<CcCiInfo> getADCIDataList(Long dataSetId, String ciCode) {
        List<ESCIInfo> data = new ArrayList<>();
        // 不需要校验 直接查ci数据
        // ESCIInfo esciInfo = this.checkCIValidity(dataSetId, ciCode);
        List<ESCIInfo> listByQuery = iamsESCIDesignSvc.getListByQuery(QueryBuilders.termQuery("ciCode.keyword", ciCode));
        if (CollectionUtils.isEmpty(listByQuery)) {
            return new ArrayList<>();
        }
        ESCIInfo esciInfo = listByQuery.get(0);
        String serialNum = esciInfo.getAttrs().get("编号").toString();
        List<ESCIInfo> appModuleData = this.getAppModuleData(serialNum);
        // 获取应用系统下的应用组件数据 包括应用系统下应用模块的组件数据 和 应用系统直接关联的组件数据
        Set<ESCIInfo> appComponentData = this.getAppComponentData(appModuleData, serialNum);
        // 获取应用系统下的应用服务数据 包括应用系统下应用模块下应用组件的服务数据 和 应用系统直接关联的服务数据
        Set<ESCIInfo> appServiceData = this.getAppServiceData(appComponentData, serialNum);
        data.addAll(appServiceData);
        // 转换数据结构 暂时写死
        List<CcCiInfo> ret = new ArrayList<>();
        CcCiClassInfo classByName = this.getCcClassByName("Draw.应用服务");
        for (ESCIInfo esciInfo1 : data) {
            ret.add(EamUtil.coverESCIInfo(esciInfo1, classByName.getAttrDefs(), classByName.getCiClass()));

        }
        return ret;
    }

    @Override
    public Boolean convertLocalViewData(List<String> ciCodes, List<String> rltCodes) {

        SysUser currentUserInfo = SysUtil.getCurrentUserInfo();
        DiagramPrivateAndDesginData diagramPrivateAndDesginData = new DiagramPrivateAndDesginData();
        List<CcCiInfo> privateNewCIDataList = new ArrayList<>();
        // 设计库ciCode不为空
        if (!BinaryUtils.isEmpty(ciCodes)) {
            /*
            *  1.根据ciCode查询设计库 和 私有库 的数据
            *       私有库的数据要用设计库业务主键进行二次查询，如果查询到数据 需要刷新用户本地的ciCode以及视图内node节点信息
            *  2.将数据按照CI检出逻辑处理
            *
            * */

            // 查询设计库数据
            CCcCi cdt = new CCcCi();
            cdt.setCiCodes(ciCodes.toArray(new String[ciCodes.size()]));
            List<CcCiInfo> designCIList = iamsESCIDesignSvc.queryCiInfoList(cdt, null, false, false);
            // 加入用户条件 查询私有库数据
            cdt.setOwnerCodeEqual(currentUserInfo.getLoginCode());
            List<CcCiInfo> privateCIList = iamsESCIPrivateSvc.queryCiInfoList(cdt, null, false, false);
            if (designCIList.size() != privateCIList.size()) {
                // 设计库ciCode查询不到的再根据业务主键查询一下
                List<String> ciPrimaryKeys = new ArrayList<>();
                List<Long> classIds = new ArrayList<>();
                List<String> privateCodeList = new ArrayList<>();
                for (CcCiInfo privateData : privateCIList) {
                    privateCodeList.add(privateData.getCi().getCiCode());
                }
                for (CcCiInfo designData : designCIList) {
                    if (!privateCodeList.contains(designData.getCi().getCiCode())) {
                        ciPrimaryKeys.add(designData.getCi().getCiPrimaryKey());
                        classIds.add(designData.getCi().getClassId());
                    }
                }
                // 这里应该不会出现业务主键list数量为空的情况
                ESCISearchBean esciSearchBean = new ESCISearchBean();
                esciSearchBean.setCiPrimaryKeys(ciPrimaryKeys);
                esciSearchBean.setClassIds(classIds);
                esciSearchBean.setOwnerCode(currentUserInfo.getLoginCode());
                esciSearchBean.setPageNum(1);
                esciSearchBean.setPageSize(ciPrimaryKeys.size());
                Page<ESCIInfo> esciInfoPage = iamsESCIPrivateSvc.searchESCIByBean(esciSearchBean);
                if (esciInfoPage.getData().size() != 0) {
                    // 这时候就需要刷一下用户本地的数据 我去 烦死了。。。。
                    List<String> conflectCIList = new ArrayList<>();
                    esciInfoPage.getData().forEach(esciInfo -> {
                        conflectCIList.add(esciInfo.getCiPrimaryKey());
                    });
                    fxDiagramSvc.freshConflectData(conflectCIList, currentUserInfo.getLoginCode()); // 刷新私有库数据之后 重新查询一下私有库CI数据
                    log.info("############ 用户本地存在业务主键冲突的数据conflectCIList：【{}】，后端统一刷新完成，重新根据设计库ciCode查询数据", JSONObject.toJSONString(conflectCIList));
                    privateCIList = iamsESCIPrivateSvc.queryCiInfoList(cdt, null, false, false);

                }
            }
            diagramPrivateAndDesginData.setDesginCiInfos(designCIList);
            diagramPrivateAndDesginData.setPrivateCiInfos(privateCIList);
            privateNewCIDataList = fxDiagramSvc.dealCheckOutDiagramCI(diagramPrivateAndDesginData);
        }

        // 设计库rltCode不为空
        if (!BinaryUtils.isEmpty(rltCodes)) {
            Set<String> rltUniqueCodes = new HashSet<>();
            rltUniqueCodes.addAll(rltCodes);
            // 关系不用处理冲突 直接根据RltCode查询私有库 设计库的数据
            ESRltSearchBean esRltSearchBean = new ESRltSearchBean();
            esRltSearchBean.setRltUniqueCodes(rltUniqueCodes);
            esRltSearchBean.setDomainId(currentUserInfo.getDomainId());
            Page<ESCIRltInfo> desginciRltInfoPage = ciRltSwitchSvc.searchRlt(esRltSearchBean, LibType.DESIGN);
            diagramPrivateAndDesginData.setDesginRltInfos(desginciRltInfoPage.getData());

            esRltSearchBean.setOwnerCode(currentUserInfo.getLoginCode());
            Page<ESCIRltInfo> privateRltInfoPage = ciRltSwitchSvc.searchRlt(esRltSearchBean, LibType.PRIVATE);
            diagramPrivateAndDesginData.setPrivateRltInfos(privateRltInfoPage.getData());

            fxDiagramSvc.dealCheckOutDiagramRLT(diagramPrivateAndDesginData, privateNewCIDataList);
        }
        return true;
    }

    private List<ESCIInfo> queryCisWithOrAttrs(List<String> orAttrsValues, String key, Long classId) {
        List<ESAttrBean> orAttrs = new ArrayList<>();
        ESAttrBean esAttrBean;
        for (String value : orAttrsValues) {
            esAttrBean = new ESAttrBean();
            esAttrBean.setKey(key);
            esAttrBean.setOptType(1);
            esAttrBean.setValue(value);
            orAttrs.add(esAttrBean);
        }
        ESCISearchBean esciSearchBean = new ESCISearchBean();
        esciSearchBean.setOrAttrs(orAttrs);
        esciSearchBean.setClassIds(Collections.singletonList(classId));
        esciSearchBean.setPageSize(1000);
        esciSearchBean.setPageNum(1);
        Page<ESCIInfo> esciInfoPage = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean);
        return esciInfoPage.getData();
    }

    private List<CiClassCiInfosVo> constructResult(List<ESCIInfo> depolyUnitCis, List<ESCIInfo> networkAreaCis, List<ESCIInfo> dataCenterCis) {
        int[] sorts = Arrays.stream(autoDeploymentArchDiagramSort.split("-")).mapToInt(Integer::parseInt).toArray();
        Map<String, Integer> sortMap = new ConcurrentHashMap<>();
        sortMap.put("DMZ", sorts[0]);
        sortMap.put("WEB", sorts[1]);
        sortMap.put("APP", sorts[2]);
        sortMap.put("DB", sorts[3]);

        //部署单元
        Map<String, Map<String, List<CiClassCiInfosVo>>> groupMap = new ConcurrentHashMap<>();
        for (ESCIInfo depolyUnitCi : depolyUnitCis) {
            String networkCode = depolyUnitCi.getAttrs().get(AutoDrawConstant.BELONGS_NETWORK_AREA).toString();
            String datacenterCode = depolyUnitCi.getAttrs().get(AutoDrawConstant.BELONGS_DATACENTER).toString();
            Map<String, List<CiClassCiInfosVo>> keyMap = groupMap.getOrDefault(datacenterCode, new ConcurrentHashMap<>());

            StringBuilder tags = new StringBuilder(networkCode)
                    .append("-")
                    .append(depolyUnitCi.getAttrs().get("部署单元类型").toString())
                    .append("-")
                    .append(depolyUnitCi.getAttrs().get("服务类型") + "");

            List<CiClassCiInfosVo> list = keyMap.getOrDefault(tags.toString(), new ArrayList<>());
            list.add(new CiClassCiInfosVo(depolyUnitCi, depolyUnitCi.getCiCode(), depolyUnitCi.getAttrs().get("中文名称").toString()));
            keyMap.put(tags.toString(), list);
            groupMap.put(datacenterCode, keyMap);
        }

        Map<String, ESCIInfo> networkAreaCiMap = networkAreaCis.stream()
                .collect(Collectors.toMap(e -> e.getAttrs().get("编号").toString(), e -> e));

        List<CiClassCiInfosVo> resultList = new ArrayList<>();
        for (ESCIInfo dataCenterCi : dataCenterCis) {
            Map<String, List<CiClassCiInfosVo>> keyMap = groupMap.getOrDefault(dataCenterCi.getAttrs().get("编号").toString(), new ConcurrentHashMap<>());
            CiClassCiInfosVo dataCenterVo = new CiClassCiInfosVo(dataCenterCi, dataCenterCi.getCiCode(), dataCenterCi.getAttrs().get("中文名称").toString());
            if (CollectionUtils.isEmpty(keyMap)) {
                dataCenterVo.setMaxCount(0);
                resultList.add(dataCenterVo);
                continue;
            }
            List<CiClassCiInfosVo> networkChilds = new ArrayList<>();
            int maxCount = 0;
            for (String tags : keyMap.keySet()) {
                //0-网络区域编号、1-部署单元类型、2-服务类型
                String tagArr[] = tags.split("-");
                //网络区域
                ESCIInfo networkCi = networkAreaCiMap.get(tagArr[0]);
                CiClassCiInfosVo networkVo = new CiClassCiInfosVo(networkCi, networkCi.getCiCode(), networkCi.getAttrs().get("中文名称").toString());
                //编号作为额外排序字段
                networkVo.setExtendsSort(tagArr[0]);
                networkVo.setSort(sortMap.getOrDefault(tagArr[1], 4));

                List<CiClassCiInfosVo> dptCis = keyMap.getOrDefault(tags, new ArrayList<>());
                dptCis.sort(Comparator.comparing(CiClassCiInfosVo::getCiCode));
                networkVo.setChildren(dptCis);
                //共有属性展示顺序：服务类型-部署单元类型
                List<String> commonTags = new ArrayList<>();
                commonTags.add(networkCi.getAttrs().get("中文名称").toString());
                if (!"null".equals(tagArr[2])) {
                    commonTags.add(tagArr[2]);
                }
                commonTags.add(tagArr[1]);
                networkVo.setCommonTags(commonTags);
                networkChilds.add(networkVo);
                maxCount = Math.max(maxCount, dptCis.size());
            }
            networkChilds.sort(Comparator.comparing(CiClassCiInfosVo::getSort).thenComparing(CiClassCiInfosVo::getExtendsSort));
            dataCenterVo.setMaxCount(maxCount);
            dataCenterVo.setChildren(networkChilds);
            resultList.add(dataCenterVo);
        }
        return resultList;
    }

    private List<String> getCiSerialNumsAttrs(List<ESCIInfo> esciInfos) {
        return esciInfos.stream().map(ESCIInfo::getAttrs)
               .filter(e -> e.get("编号") != null).map(e -> e.get("编号").toString()).collect(Collectors.toList());
    }

    /**
     *  根据分类名称获取分类信息
     * @return
     */
    private ESCIClassInfo getClassByName(String className) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("className.keyword", className));
        List<ESCIClassInfo> listByQuery = esciClassSvc.getListByQuery(boolQueryBuilder);
        if (CollectionUtils.isEmpty(listByQuery)) {
            throw new BinaryException("分类名称不存在");
        }
        return listByQuery.get(0);
    }

    /**
     *  根据分类名称获取分类信息
     * @return
     */
    private CcCiClassInfo getCcClassByName(String className) {
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassCodeEqual(className);
        List<CcCiClassInfo> ccCiClassInfos = esciClassSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(ccCiClassInfos)) {
            throw new BinaryException("分类名称不存在");
        }
        return ccCiClassInfos.get(0);
    }

    /**
     *  根据应用系统编号查询应用模块数据
     * @param serialNum
     * @return
     */
    public List<ESCIInfo> getAppModuleData(String serialNum) {
        List<ESCIInfo> data = new ArrayList<>();
        ESCISearchBean esciSearchBean = new ESCISearchBean();
        ESAttrBean esAttrBean = new ESAttrBean();
        esAttrBean.setKey(AutoDrawConstant.BELONGS_APP_SYSTEM);
        esAttrBean.setOptType(1);
        esAttrBean.setValue(serialNum);
        esciSearchBean.setAndAttrs(Collections.singletonList(esAttrBean));
        esciSearchBean.setClassIds(Collections.singletonList(getClassIdByName("Draw.应用模块")));
        esciSearchBean.setPageSize(500);
        esciSearchBean.setPageNum(1);
        esciSearchBean.setSortField("attrs.编号.keyword");
        esciSearchBean.setAsc(true);
        Page<ESCIInfo> esciInfoPage = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean);
//        List<ESCIInfo> dataOne = esciInfoPage.getData().stream().filter(esciInfo -> BinaryUtils.isEmpty(esciInfo.getAttrs().get("上级应用模块"))).collect(Collectors.toList());
//        data.addAll(dataOne);
//        // 系统模块数据本身含有关系 数据需要循环查询 直至查询到最后一层级
//        if (!CollectionUtils.isEmpty(dataOne)) {
//            boolean flag = true;
//            while (flag) {
//                List<ESCIInfo> appModuleDataByMId = this.getAppModuleDataByMId(dataOne);
//                if (!CollectionUtils.isEmpty(appModuleDataByMId)) {
//                    // this.getAppModuleDataByMId(appModuleDataByMId);
//                    data.addAll(appModuleDataByMId);
//                } else {
//                    flag = false;
//                }
//            }
//        }

        return esciInfoPage.getData();
    }

    /**
     *  根据应用模块查询应用组件数据 没有的话根据应用系统查询
     * @param appModuleData
     * @param serialNum
     * @return
     */
    public Set<ESCIInfo> getAppComponentData(List<ESCIInfo> appModuleData, String serialNum) {
        Set<ESCIInfo> data = new HashSet<>();
        if (!CollectionUtils.isEmpty(appModuleData)) {
            // 不为空 根据应用模块继续查询技术组件
            ESCISearchBean esciSearchBean1 = new ESCISearchBean();
            List<ESAttrBean> attrBeans = new ArrayList<>();
            for (ESCIInfo esciInfo1 : appModuleData) {
                ESAttrBean esAttrBean1 = new ESAttrBean();
                esAttrBean1.setKey("所属应用模块");
                esAttrBean1.setOptType(1);
                esAttrBean1.setValue(esciInfo1.getAttrs().get("编号"));
                attrBeans.add(esAttrBean1);
            }
            esciSearchBean1.setOrAttrs(attrBeans);
            esciSearchBean1.setClassIds(Collections.singletonList(getClassIdByName("Draw.技术组件")));
            esciSearchBean1.setPageSize(500);
            esciSearchBean1.setPageNum(1);
            esciSearchBean1.setSortField("attrs.编号.keyword");
            esciSearchBean1.setAsc(true);
            Page<ESCIInfo> esciInfoPage1 = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean1);
            data.addAll(esciInfoPage1.getData());
        }
        // 为空 根据应用系统查询技术组件
        ESCISearchBean esciSearchBean2 = new ESCISearchBean();
        ESAttrBean esAttrBean2 = new ESAttrBean();
        esAttrBean2.setKey(AutoDrawConstant.BELONGS_APP_SYSTEM);
        esAttrBean2.setOptType(1);
        esAttrBean2.setValue(serialNum);
        esciSearchBean2.setAndAttrs(Collections.singletonList(esAttrBean2));
        esciSearchBean2.setClassIds(Collections.singletonList(getClassIdByName("Draw.技术组件")));
        esciSearchBean2.setPageSize(500);
        esciSearchBean2.setPageNum(1);
        esciSearchBean2.setSortField("attrs.编号.keyword");
        esciSearchBean2.setAsc(true);
        Page<ESCIInfo> esciInfoPage2 = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean2);
        data.addAll(esciInfoPage2.getData());
        return data;
    }

    /**
     *  根据应用组件查询应用服务数据 没有的话根据应用系统查询
     * @param appComponentData
     * @param serialNum
     * @return
     */
    public Set<ESCIInfo> getAppServiceData(Set<ESCIInfo> appComponentData, String serialNum) {
        Set<ESCIInfo> data = new HashSet<>();
        if (!CollectionUtils.isEmpty(appComponentData)) {
            // 不为空 根据应用组件继续查询服务
            ESCISearchBean esciSearchBean1 = new ESCISearchBean();
            List<ESAttrBean> attrBeans = new ArrayList<>();
            for (ESCIInfo esciInfo1 : appComponentData) {
                ESAttrBean esAttrBean1 = new ESAttrBean();
                esAttrBean1.setKey("所属应用组件");
                esAttrBean1.setOptType(1);
                esAttrBean1.setValue(esciInfo1.getAttrs().get("编号"));
                attrBeans.add(esAttrBean1);
            }
            esciSearchBean1.setOrAttrs(attrBeans);
            esciSearchBean1.setClassIds(Collections.singletonList(getClassIdByName("Draw.应用服务")));
            esciSearchBean1.setPageSize(500);
            esciSearchBean1.setPageNum(1);
            Page<ESCIInfo> esciInfoPage1 = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean1);
            data.addAll(esciInfoPage1.getData());
        }
        // 为空 根据应用系统查询技术组件
        ESCISearchBean esciSearchBean2 = new ESCISearchBean();
        ESAttrBean esAttrBean2 = new ESAttrBean();
        esAttrBean2.setKey(AutoDrawConstant.BELONGS_APP_SYSTEM);
        esAttrBean2.setOptType(1);
        esAttrBean2.setValue(serialNum);
        esciSearchBean2.setAndAttrs(Collections.singletonList(esAttrBean2));
        esciSearchBean2.setClassIds(Collections.singletonList(getClassIdByName("Draw.应用服务")));
        esciSearchBean2.setPageSize(500);
        esciSearchBean2.setPageNum(1);
        Page<ESCIInfo> esciInfoPage2 = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean2);
        data.addAll(esciInfoPage2.getData());
        return data;
    }

    /**
     *  根据应用模块查询下级应用模块数据getTechnologyArchDiagramData
     * @param moduleCiList
     * @return
     */
    public List<ESCIInfo> getAppModuleDataByMId(List<ESCIInfo> moduleCiList) {
        ESCISearchBean esciSearchBean1 = new ESCISearchBean();
        List<ESAttrBean> attrBeans = new ArrayList<>();
        for (ESCIInfo esciInfo1 : moduleCiList) {
            ESAttrBean esAttrBean1 = new ESAttrBean();
            esAttrBean1.setKey("上级应用模块");
            esAttrBean1.setOptType(1);
            esAttrBean1.setValue(esciInfo1.getAttrs().get("编号"));
            attrBeans.add(esAttrBean1);
        }
        esciSearchBean1.setOrAttrs(attrBeans);
        esciSearchBean1.setClassIds(Collections.singletonList(getClassIdByName("Draw.应用模块")));
        esciSearchBean1.setPageSize(500);
        esciSearchBean1.setPageNum(1);
        Page<ESCIInfo> esciInfoPage1 = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean1);
        return esciInfoPage1.getData();
    }

    /**
     *  获取应用服务下的服务接口数据
     * @param appServiceData
     * @return
     */
    public List<ESCIInfo> getAppInterfaceData(Set<ESCIInfo> appServiceData) {
        if (CollectionUtils.isEmpty(appServiceData)) {
            return new ArrayList<>();
        }
        ESCISearchBean esciSearchBean1 = new ESCISearchBean();
        List<ESAttrBean> attrBeans = new ArrayList<>();
        for (ESCIInfo esciInfo1 : appServiceData) {
            ESAttrBean esAttrBean1 = new ESAttrBean();
            esAttrBean1.setKey("所属应用服务");
            esAttrBean1.setOptType(1);
            esAttrBean1.setValue(esciInfo1.getAttrs().get("编号"));
            attrBeans.add(esAttrBean1);
        }
        esciSearchBean1.setOrAttrs(attrBeans);
        esciSearchBean1.setClassIds(Collections.singletonList(getClassIdByName("Draw.应用接口")));
        esciSearchBean1.setPageSize(500);
        esciSearchBean1.setPageNum(1);
        Page<ESCIInfo> esciInfoPage1 = iamsESCIDesignSvc.searchESCIByBean(esciSearchBean1);
        return esciInfoPage1.getData();
    }
}
